﻿using ArienToolkits.Exceptions;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;

namespace ArienToolkits.ReflectionUnilities
{
    /// <summary>
    /// 类型转换器
    /// </summary>
    public class TypeConverter
    {
        /// <summary>
        /// 将数据表转换为集合对象
        /// </summary>
        /// <typeparam name="T">目标集合限定类型</typeparam>
        /// <param name="table">数据表</param>
        /// <param name="withoutFields">排除列</param>
        /// <returns></returns>
        public static IList<T> ToList<T>(DataTable table, params string[] withoutFields)
        {
            if (table == null)
            {
                return null;
            }

            IList<T> resultList = new List<T>();

            foreach (DataRow row in table.Rows)
            {
                resultList.Add(ToObject<T>(row, withoutFields));
            }

            return resultList;
        }

        /// <summary>
        /// 将数据行转换为对象
        /// </summary>
        /// <typeparam name="T">目标类型</typeparam>
        /// <param name="row">数据行</param>
        /// <param name="withoutFields">排除列</param>
        /// <returns></returns>
        public static T ToObject<T>(DataRow row, params string[] withoutFields)
        {
            if (row == null)
            {
                return default(T);
            }

            DataTable table = row.Table;
            Type targetType = typeof(T);
            T target = Activator.CreateInstance<T>();

            foreach (DataColumn column in table.Columns)
            {
                //如果有排除字段，则跳过
                if (withoutFields != null && withoutFields.Contains(column.ColumnName))
                {
                    continue;
                }

                PropertyInfo property = targetType.GetProperty(column.ColumnName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                if (property == null)
                {
                    throw new PropertyNotFoundException(targetType.FullName, column.ColumnName);
                }

                Type propertyType = property.PropertyType;

                object obj = null;
                bool flag = true;
                try
                {
                    obj = ChangeType(propertyType, row[column.ColumnName]);
                }
                catch
                {
                    flag = false;
                }

                if (flag)
                {
                    object[] args = new object[] { obj };
                    targetType.InvokeMember(column.ColumnName, BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase, null, target, args);
                }
            }

            return target;
        }

        /// <summary>
        /// 调整对象类型
        /// </summary>
        /// <param name="targetType"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        public static object ChangeType(Type targetType, object val)
        {
            if (val == null || (targetType.IsGenericType && val.ToString() == ""))
            {
                return null;
            }
            if (targetType == val.GetType() || targetType.IsGenericType)
            {
                return val;
            }
            if (targetType == typeof(bool))
            {
                return (val.ToString() != "0");
            }
            if (targetType.IsEnum)
            {
                int result = 0;
                if (!int.TryParse(val.ToString(), out result))
                {
                    return Enum.Parse(targetType, val.ToString());
                }
                return val;
            }
            if (targetType == typeof(Type))
            {
                return ReflectionHelper.GetType(val.ToString());
            }
            return Convert.ChangeType(val, targetType);
        }

        /// <summary>
        /// 验证指定类型是否为数值类型
        /// </summary>
        /// <param name="destDataType">目标数据类型</param>
        /// <returns></returns>
        public static bool IsNumbericType(Type destDataType)
        {
            return ((((((destDataType == typeof(int)) || (destDataType == typeof(uint))) || ((destDataType == typeof(double)) || (destDataType == typeof(short)))) || (((destDataType == typeof(ushort)) || (destDataType == typeof(decimal))) || ((destDataType == typeof(long)) || (destDataType == typeof(ulong))))) || ((destDataType == typeof(float)) || (destDataType == typeof(byte)))) || (destDataType == typeof(sbyte)));
        }
    }
}
